#include <QNetworkAccessManager>
#include <QCoreApplication>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDir>
#include <QDebug>

#include "download.h"

#define QT_NO_DEBUG_OUTPUT

Download::Download(QObject *parent) : QObject(parent)
{
    this->init();
}

Download::Download(QList<file_t> &fileDataList,
                   QString currentBaseDir,
                   QString downloadDir,
                   QObject *parent) : QObject(parent)
{
    /* 异步通信的临时变量可能会调用者被释放从而访问的时候导致野引用问题。 */
//    this->fileDataList = fileDataList;

    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    for(int i = 0; i < fileDataList.size(); i++) {
        this->fileDataList.append(fileDataList.at(i));
    }
    this->init();
}

Download::Download(file_t &fileData,
                   QString currentBaseDir,
                   QString downloadDir,
                   QObject *parent) : QObject(parent)
{
    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    this->fileDataList.append(fileData);
    this->init();
}

Download::Status Download::init()
{
    this->downloadFinishedCount = 0;
    this->downloadFileListSize = this->fileDataList.size();
    this->settings = new ConfigFile(QCoreApplication::applicationDirPath() + "/" + DEFAULT_FILENAME);

    /* init network env */
    this->downloading = false;
    this->netManager = new QNetworkAccessManager();
    connect(this->netManager,
            &QNetworkAccessManager::finished,
            this,
            &Download::downloadResponse);
    this->baseRequest = new QNetworkRequest();
    baseRequest->setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                                                        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome"
                                                        "/104.0.0.0 Safari/537.36");
    baseRequest->setRawHeader("Host", settings->value(USER_WEB_URL).toString().mid(7).toUtf8());
    baseRequest->setRawHeader("Referer", QString("%1/index.php")
                         .arg(settings->value(USER_WEB_URL).toString()).toUtf8());
    QList<QByteArray> requestHeaderList = baseRequest->rawHeaderList();

    qDebug() << "Print the parameters of the request header:";
    for(int i = 0; i < requestHeaderList.size(); i++) {
        qDebug().noquote() << requestHeaderList.at(i) << ":" << baseRequest->rawHeader(requestHeaderList.at(i));
    }
    return NO_ERROR;
}

Download::~Download()
{
    this->fileDataList.clear();
    delete this->settings;
    delete this->netManager;
    delete this->baseRequest;
}

void Download::reset()
{
    this->fileDataList.clear();
    this->downloadDir = "";
    this->currentBaseDir = "";
    qDebug() << "Download::" << __FUNCTION__ << "Done.";
}

void Download::set(QList<file_t> &fileDataList, QString currentBaseDir, QString downloadDir)
{
    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    for(int i = 0; i < fileDataList.size(); i++) {
        this->fileDataList.append(fileDataList.at(i));
    }
}

void Download::set(file_t &fileData, QString currentBaseDir, QString downloadDir)
{
    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    this->fileDataList.append(fileData);
}

Download::Status Download::startDownload()
{
    /* The incoming data is not checked for validity in the download thread. */

    if(this->downloadDir.length() == 0 || this->currentBaseDir.length() == 0) {
        qDebug() << "Call the set method first to set the network path or local path.";
        return UNDEFINED_ERROR;
    }

    this->name = this->settings->value(USER_NAME).toString();
    this->phpSessionID = this->settings->value(USER_PHPSESSID).toString();
    if(this->name.length() == 0)          { return INVALID_NAME; }
    if(this->phpSessionID.length() == 0)  { return INVALID_PHPSESSID; }
    baseRequest->setRawHeader("Cookie", QString("PHPSESSID=%1; secros_user_language=zh_CN; secros_name=%2")
                         .arg(this->phpSessionID)
                         .arg(this->name)
                         .toUtf8());

    if(this->fileDataList.length() == 0 || this->fileDataList.size() == 0) {
        return INVALID_FILE_DATA_LIST;
    }

    if(!QDir(this->downloadDir).exists()) {
        return INVALID_DIR;
    }

    for(int i = 0; i < this->fileDataList.size(); i++) {
        file_t fileData = this->fileDataList.at(i);
        if(!fileData.isFile) {
            qDebug() << "Download::" << __FUNCTION__ << QString("'%1' is folder").arg(fileData.name);
            return FILE_IS_FOLDER;
        }
    }

    /* set download */
    this->downloading = true;
    QString url;
    QNetworkRequest req = *(this->baseRequest);
    url += settings->value(USER_WEB_URL).toString();
    url += QString("/index.php?explorer/fileDownload&path=%1%2")
            .arg(this->currentBaseDir)
            .arg(this->fileDataList.takeFirst().name);
    req.setUrl(QUrl(url));
    qDebug("Start request '%s' .\n", url.toLatin1().data());

    QList<QByteArray> requestHeaderList = baseRequest->rawHeaderList();
    qDebug() << "Print the parameters of the request header:";
    for(int i = 0; i < requestHeaderList.size(); i++) {
        qDebug().noquote() << requestHeaderList.at(i) << ":" << baseRequest->rawHeader(requestHeaderList.at(i));
    }

    this->netManager->get(req);

    return NO_ERROR;
}

bool Download::isDownloading()
{
    return this->downloading;
}

void Download::downloadResponse(QNetworkReply *reply)
{
    qDebug() << __FUNCTION__;
    /* 过滤请求错误 */
    if(reply->error() != QNetworkReply::NoError) {
        emit finished(RESPONSE_ERROR, reply->errorString());
        return ;
    }

    /* 判断响应数据 */
    QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    /* 如果解析响应状态码错误或者状态码!=200 */
    qDebug("%s Response statusCode:%d", __FUNCTION__, statusCode.toInt());
    if(!statusCode.isValid() || statusCode.toInt() != 200) {
        QVariant reason =  reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        this->downloading = false;
        if(reason.isValid())
            emit finished(RESPONSE_ERROR, reason.toString());
        else
            emit finished(RESPONSE_ERROR, QString("%1 Unknown response error").arg(__FUNCTION__));
        return ;
    }

    /* parse response data */
    qDebug().noquote() << reply;
    qDebug("Print the parameters of the response header:");
    QList<QByteArray> replyHeadersList = reply->rawHeaderList();
    for(int i = 0; i < replyHeadersList.size(); i++) {
        QByteArray headerByteArray = replyHeadersList.at(i);
        qDebug("%s : %s",
               headerByteArray.data(),
               reply->rawHeader(headerByteArray).data());
    }
    QString contentDisposition_Header = QString(reply->rawHeader("Content-Disposition"));
    int fileNameIndex = contentDisposition_Header.indexOf("filename=");
    fileNameIndex += QString("filename=").length();
    QString fileName = contentDisposition_Header.mid(fileNameIndex);
    QString downloadPath = this->downloadDir + "/" + fileName;
    qDebug("%s Downloaded file path: %s", __FUNCTION__, downloadPath.toLatin1().data());

    /* save data to file */
    QFile file(downloadPath);
    bool isOpen = file.open(QIODevice::WriteOnly);
    if(!isOpen) {
        qDebug("%s Open '%s' error:%s",
               __FUNCTION__,
               downloadPath.toLatin1().data(),
               file.errorString().toUtf8().data());
        this->downloading = false;
        emit finished(RESPONSE_ERROR, file.errorString());
        return ;
    }
    int writeLen = file.write(reply->readAll());
    qDebug("%s '%s' writed %d bytes.", __FUNCTION__, downloadPath.toLatin1().data(), writeLen);
    qDebug("%s QFile.size=%lld", __FUNCTION__,  file.size());
    file.flush();
    file.close();
    qDebug("\n");

    this->downloadFinishedCount++;
    if(this->fileDataList.isEmpty() || downloadFinishedCount == downloadFileListSize) {
        qDebug("Downloading Done.");
        this->downloading = false;
        emit finished(NO_ERROR, this->errorString(NO_ERROR));
        return ;
    }

//    QThread::usleep(500 * 1000); // 爬虫缓冲
    QString url;
    QNetworkRequest req = *baseRequest;
    url += settings->value(USER_WEB_URL).toString();
    url += QString("/index.php?explorer/fileDownload&path=%1%2")
            .arg(this->currentBaseDir)
            .arg(this->fileDataList.takeFirst().name);
    req.setUrl(QUrl(url));
    printf("Start request '%s' .", url.toLatin1().data());
    netManager->get(req);
}

